// 防抖
function debounce(fn,delay){
    let timer=null
    return  (...arg)=>{
        if (timer){
            clearTimeout(timer)
            timer=null
        }
        timer=setTimeout(()=>{
            fn(...arg)
            clearTimeout(timer)
            timer=null

        },delay)
    }
}

// 节流
function throttle(fn,delay){
    let timer=null
    return function (...arg){
        if (timer){
           return
        }
        timer=setTimeout(()=>{
            fn(...arg)
            clearTimeout(timer)
            timer=null

        },delay)
    }
}

// 深拷贝（基础版）
function deepClone(target){
    if(typeof target!=='object'||target===null){
        return target;
    }

    const copyTarget=Array.isArray(target)?[]:{} // 本层创建

    for (let key in target){
        copyTarget[key]=deepClone(target[key])
    }

    return copyTarget
}

// 深拷贝（处理循环引用版）
// 核心是每次递归的是对象类型，就使用WeakMap保存
function deepClonePlus(target,hash){
    if(target!=='object'||target===null){
        return target
    }

    if (hash.has(target)){
        return hash.get(target)
    }

    const copyTarget= Array.isArray(target)?[]:{}

    for (let key in target){
        copyTarget[key]=deepClone(target[key],hash)
    }
    hash.set(target,copyTarget) // copyTarget就是复制本层对象

    return target
}
// const obj1 = {};
// const obj2 = { a: 1 };
// obj1.obj2 = obj2;
// obj2.obj1 = obj1;
// const obj3 = deepClonePlus(obj1);
// console.log(obj3.obj2.a); // 输出 1


// 比较两个对象
function isEqual(){

}


// 判读类型（衍生出类型相关的题目：实现isArray判断数组  ）
function getType(target){
    return Object.prototype.toString.call(target).match(/\[object (.*)\]/)[1]
}


// 去掉收尾空格
function trim(str){
   return  str.replace(/^\s+|\s+$/g,'')
}

// flat 扁平化数组
// example: [1,2,[3]] ==> [1,2,3] 。可以设置展开多层
// list.concat方法 入参是数组可打开合并list，入参是元素直接加入list
function flatArray(list,depth){
    // 最后一层
    if (depth===0){
        return list
    }
    // 其余层
    return list.reduce((pre,cur)=>{
        pre.concat(Array.isArray(cur)?flatArray(cur,depth-1):cur)
    },[])
}
// 换用自己熟悉的模版
function flatArray2(arr,depth){

    //
    if(depth===0){
        return arr
    }

    //
    let res=[]
    for(let item of arr){
        if(Array.isArray(item)){
            res.push(...item)
        }else {
            res.push(item)
        }
    }

    return flatArray(res,depth-1)

}



// Promise.all 实现
function all(promiseList){
    return new Promise((resolve,reject)=>{
        let len=promiseList.length
        let resolveList=[]
        let count=0
        for (let i=0;i< len;i++){
            // 如果promiseList[i]不是promise就报错了，所以这里用 Promise.resolve 包下
            Promise.resolve(promiseList[i]).then((data)=>{
                resolveList[i]=data // 并发执行，必须i用对应上
            }).catch(err=>{
                reject(err)
            }).finally(()=>{
                count++
                if (count===len){ // 并发执行，i===len时不一定是全部执行完了。
                    return resolve(resolveList)
                }
            })
        }


    })
}
function allSettle(PromiseList){
    return new Promise((resolve)=>{
        let result=[]
        let count=0
        for (let index in PromiseList){
            Promise.resolve(PromiseList).then((res)=>{
                result[index]={status:'fulfilled',value:res}
            }).catch(err=>{
                result[index]={status:'rejected',value:err.message}
            }).finally(()=>{
                count++
                if (count===PromiseList.length){
                    resolve(result)
                }
            })
        }
    })
}
function race (promiseList)  {
    return new Promise((resolve, reject) => {
        // 循环执行，第一个resolve，其他的在resolve也没用了
        promiseList.forEach((item) => {
            Promise.resolve(item).then(
                (val) => {
                    resolve(val);
                },
                (reason) => {
                    reject(reason);
                },
            );
        });
    });
};

// 重试逻辑
function requestRetry(url,retries=3){
    return new Promise(async (resolve,reject)=>{
        let count=1
        function attempt(){
            // 其余层
            fetch(url).then(res=>{
                if(!res.ok){
                   throw new Error('请求异常') // http状态码不在 200-299之间
                }
                return resolve(res.json())
            }).catch(err=>{
                // 最后一层
                if (count<retries){
                    count++
                    attempt()
                }else {
                    return reject(err)
                }

            })
        }

        attempt()

    })
}

// 控制promise的并发
// function createRequestPool(limit){
//     const queue=[] //
//     const runningQueue=new Set() // 运行队列
//     function run(){
//         if(runningQueue.size>limit){ // 运行队列已满
//             return
//         }
//
//         const wantage=limit-runningQueue.size
//
//         // queue移除前wantage个元素，返回移除的元素
//         queue.splice(0,wantage).forEach(func=>{
//             runningQueue.add(func)
//             func().then(()=>{
//                 runningQueue.delete(func)
//                 run()
//             })
//         })
//     }
//
//     // 闭包
//     return ()=>{
//         return new Promise(resolve => {
//             queue.push(()=>request().then((result)=>{
//                 resolve(result)
//                 return result
//             }))
//             run()
//         })
//     }
// }

// 订阅发布
class EventBus {
    constructor() {
        this.events = {}; // 存储事件及其对应的回调函数列表
    }

    // 订阅事件
    subscribe(eventName, callback) {
        this.events[eventName] = this.events[eventName] || []; // 如果事件不存在，创建一个空的回调函数列表
        this.events[eventName].push(callback); // 将回调函数添加到事件的回调函数列表中
    }

    // 发布事件
    publish(eventName, data) {
        if (this.events[eventName]) {
            this.events[eventName].forEach(callback => {
                callback(data); // 执行回调函数，并传递数据作为参数
            });
        }
    }

    // 取消订阅事件
    unsubscribe(eventName, callback) {
        if (this.events[eventName]) {
            this.events[eventName] = this.events[eventName].filter(cb => cb !== callback); // 过滤掉要取消的回调函数
        }
    }
}

// 随机生成字符串
function randomStr(){
    return Math.random().toString(32).slice(2) // 0.msdi497fvk 从索引2开始截取，共十位
}


// 简易路由  vue的hash==> location.hash  hashChange事件、 history ===> history.pushState  popstate事件


// 通过 setTimeout 实现 setInterval
function mySetInterval(callback, delay) {
    let timeoutId;

    function tick() {
        callback();
        timeoutId = setTimeout(tick, delay);
    }

    timeoutId = setTimeout(tick, delay); // 启动一个递归。如果想要一开始就执行callback，这里换成tick()

    return {
        cancel:()=> {
            clearTimeout(timeoutId);
            timeoutId=null
        }
    };
}

// 使用示例
const interval = mySetInterval(() => {
    console.log('Hello, world!');
}, 1000);

// 取消定时器
interval.cancel();


// bind实现。bind每次调用不是立即执行，而是返回一个函数，这是典型的闭包
Function.prototype.fakeBind=(obj,...args)=>{
    return (...rest)=>this.call(obj,...args,...rest) // this指向window，window上有call方法
}

// 无限累加的sum
function sum(...args){
    // 闭包，但是不直接返回函数，而是在函数上挂calc函数，再返回
    // fn的参数就是sum(1)返回的函数的参数
    const fn=(...rest)=>sum(...args,...rest)
    // 这里的args就是合并后的。调用calc进行计算
    fn.calc=()=> args.reduce((pre,cur)=>pre+cur,0)
    return fn
}
// console.log(sum(1)(2)(3,7).calc()) // 13
// 珂里化
function curry(fn, ...args) {
    return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}
// 无限累加的sum、珂里化两个题很有特点。 无限累加是通过闭包返回新函数实现多次调用，珂里化使用的是bind而bind的原理就是闭包
// bind 有一种神奇的能力。bind每次都返回一个新的原函数，这样就能实现参数累加的功能
// curry是对fn的封装实现通用的珂里化函数，下面是对具体函数test柯里化
// function test(...arg) {
//     console.log(arg)
// }
// test.bind(null,1)(2) // [1, 2]



// 数组乱序
const shuffle = (list) => list.sort((x, y) => Math.random() - 0.5)


// 通过parentId构建有层次的对象
function buildTree(array) {
    // 创建一个哈希表来存储每个节点及其子节点
    const nodesById = {};
    // 存储顶级节点
    const rootNodes = [];

    // 遍历数组，构建节点和子节点列表
    array.forEach(item => {
        const node = {
            id: item.id,
            parentId: item.parentId,
            children: []
        };

        // 将节点添加到哈希表中
        nodesById[item.id] = node;

        // 如果节点有父节点，则将它添加到父节点的子节点列表中
        if (item.parentId !== null) {
            const parentNode = nodesById[item.parentId];
            if (parentNode) {
                parentNode.children.push(node);
            }
        } else {
            // 如果节点没有父节点，则将其添加到顶级节点列表中
            rootNodes.push(node);
        }
    });

    // 返回顶级节点组成的数组
    return rootNodes;
}

// 示例数据
const data = [
    { id: 1, parentId: null }, // 顶级节点
    { id: 2, parentId: 1 },    // 节点 1 的子节点
    { id: 3, parentId: 1 },    // 节点 1 的另一个子节点
    { id: 4, parentId: 2 },    // 节点 2 的子节点
    { id: 5, parentId: 2 },    // 节点 2 的另一个子节点
    { id: 6, parentId: 5 },    // 节点 5 的子节点
];

// const tree = buildTree(data);



// 下载文件
function download(data,type,filename){
    const blob=  new Blob([data],{type})
    const url =  URL.createObjectURL(blob)

    const downloadEle= document.createElement('a')
    downloadEle.href=url
    downloadEle.style.display='none'
    downloadEle.download=filename
    document.body.appendChild(downloadEle)
    downloadEle.click()
    document.body.removeChild(downloadEle)
    URL.revokeObjectURL(url)

}


// 加载依赖。 解决环形引用
class taskManager{
    constructor(){
        this.list=[]
        this.historyRecord={}
    }

    addTask(taskFunc){
        this.list.push(taskFunc)
    }

    analysisDependencies(taskFunc){
        return []
    }
    execute(){
        this.dfsAnalysis(this.list)
    }

    // 递归解析依赖
    dfsAnalysis(taskFuncList){

        for (let taskFunc of taskFuncList){
            // 解析依赖
            const dependenciesList=this.analysisDependencies(taskFunc)

            // 检查依赖是否循环引用
            if(this.checkCycle(dependenciesList)){
                throw new Error('存在循环引用')
            }

            // 递归下一层解析依赖
            this.dfsAnalysis(dependenciesList)
            // 执行本层
            if (this.historyRecord[taskFunc]){ // 运行过，直接结束
                return
            }
            taskFunc()

            this.historyRecord[taskFunc]=true

        }


    }

    // 检测依赖列表中是否存在环形依赖
    checkCycle(dependencies){
        for (let dep of dependencies){
            if(this.historyRecord[dep]){
                return true
            }
        }
        return false

    }

}

// 核心
class LRUCache {
    constructor(limit) {
        this.limit = limit;
        this.cache = new Map();
    }

    get(key) {
        if (!this.cache.has(key)){
            return undefined
        }
        const value = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, value);
        return value;
    }

    set(key, value) {
        if (this.cache.has(key)){
            this.cache.delete(key)
        }else if (this.cache.size >= this.limit) {
            this.cache.delete(this.cache.keys().next().value); //this.cache.keys().next() 返回开头的元素，结构为 {value: 'd', done: false}
        }
        this.cache.set(key, value);
    }
}

// const lruCache = new LRUCache(2);
// lruCache.set(1, 1);
// lruCache.set(2, 2);
// lruCache.get(1);
// lruCache.set(3, 3);
// console.log(lruCache.get(1),lruCache.get(2),lruCache.get(3)); // 1 undefined 3 。最近被使用的是1，所以2被移除了

// 实现一个pipe方法，可以实现异步函数串行执行
function pipe(...funcList){
    funcList.reduce((pre,cur)=>{
       return pre.then(cur)  // 注意： 每个pre都是一个Promise，不能写成pre()
    },Promise.resolve())
}

async function pipe2(...funcList){
    for (let func of funcList){ // 注意这个特性。for循环中使用await会阻塞循环，会等待一个一个await执行完毕
        await func()
    }
}

// 每次获取一个对象的属性时，都会打印获取对象xxx属性，比如获取obj.a.b，如何实现
function watchProperty(target){
    const handler={
        get(target,key,receiver){
            console.log(`获取${key}属性`,receiver)
            const val = Reflect.get(target,key,receiver) // receiver就是target，使用这个属性后，当触发target的getter时，如果使用this，this会指向target代理对象
            if (val instanceof Object){
                return new Proxy(val,handler)
            }
            return val
        }
    }

    return new Proxy(target,handler)
}

const watchPropertyProxy=watchProperty({
    first:{
        gender:'man',
        score:{
            math:100
        }
    }
})
console.log(watchPropertyProxy.first.score.math)

// proxy监听对象
// let product={
//     price:10,
//     count:2
// }
//
// let total=0
//
// let effect=()=>{
//     total=proxyProduct.price*proxyProduct.count
//     console.log('总价格',total)
// }
//
// effect()
//
// const proxyProduct=new Proxy(product,{
//     set(target,key,newVal,receiver){ // receiver就是proxyProduct，是一个代理对象
//         target[key]=newVal
//         effect()
//         return true
//     },
//     get(target, key, receiver) {
//         return target[key]
//     }
// })



